import * as Tone from 'tone';
import cn from 'classnames';
import { useCallback, useEffect, useState, useRef } from 'react';
import styles from './index.module.scss';

export interface MusicalNoteType {
    name: string;
    type: string;
    keyboard: string;
}

const musicalNoteList = [
    /** 高音 */
    { name: 'do', type: 'C1', keyboard: 'Q' },
    { name: 're', type: 'C2', keyboard: 'W' },
    { name: 'mi', type: 'C3', keyboard: 'E' },
    { name: 'fa', type: 'C4', keyboard: 'R' },
    { name: 'so', type: 'C5', keyboard: 'T' },
    { name: 'la', type: 'C6', keyboard: 'Y' },
    { name: 'ti', type: 'C7', keyboard: 'U' },

    /** 中音 */
    { name: 'do', type: 'B1', keyboard: 'A' },
    { name: 're', type: 'B2', keyboard: 'S' },
    { name: 'mi', type: 'B3', keyboard: 'D' },
    { name: 'fa', type: 'B4', keyboard: 'F' },
    { name: 'so', type: 'B5', keyboard: 'G' },
    { name: 'la', type: 'B6', keyboard: 'H' },
    { name: 'ti', type: 'B7', keyboard: 'J' },

    /** 低音 */
    { name: 'do', type: 'A1', keyboard: 'Z' },
    { name: 're', type: 'A2', keyboard: 'X' },
    { name: 'mi', type: 'A3', keyboard: 'C' },
    { name: 'fa', type: 'A4', keyboard: 'V' },
    { name: 'so', type: 'A5', keyboard: 'B' },
    { name: 'la', type: 'A6', keyboard: 'N' },
    { name: 'ti', type: 'A7', keyboard: 'M' },
];

const keyboardMap = musicalNoteList.reduce((acc, cur) => {
    acc.set(cur.keyboard, cur);
    return acc;
}, new Map<string, MusicalNoteType>());

export interface MusicalNoteItemProps extends MusicalNoteType {
    /** 当前电机的键盘按键 */
    curKeyboard: string;
    className?: string;
}

const MusicalNoteItem = (props: MusicalNoteItemProps) => {
    const { className, name, curKeyboard, keyboard } = props;

    return (
        <div className={cn(styles.keyboard, keyboard === curKeyboard && styles.active, className)}>
            <div>{keyboard}</div>
            <div>{name}</div>
        </div>
    );
}

const YuanShenPinao = () => {
    const [keyboard, setKeyBoard] = useState('');
    const toneSynthRef = useRef<Tone.Synth<Tone.SynthOptions>>();

    const onKeyDown = useCallback((e: KeyboardEvent) => {
        initToneSynth();
        const upperKey = e.key.toUpperCase();
        const musicalNode = keyboardMap.get(upperKey);
        if (musicalNode) {
            setKeyBoard(upperKey);
            /** 制造音频 */
            const now = Tone.now();
            toneSynthRef.current?.triggerAttackRelease(musicalNode.type, '8n', now);
        }
    }, []);

    const onKeyUp = useCallback(() => {
        setKeyBoard('');
    }, []);

    const initToneSynth = useCallback(() => {
        if (!toneSynthRef.current) {
            toneSynthRef.current = new Tone.Synth();
            toneSynthRef.current.toDestination();
        }
        if (Tone.context.state !== 'running') {
            Tone.context.resume();
        }
    }, []);

    useEffect(() => {
        document.addEventListener('keyup', onKeyUp, false);
        document.addEventListener('keydown', onKeyDown, false);
        return () => {
            document.removeEventListener('keyup', onKeyUp, false);
            document.removeEventListener('keydown', onKeyDown, false);
        }
    }, []);

    return (
        <div className={styles.container}>
            <div className={styles.musicalNoteBox}>
                <div className={styles.musicalNoteLabel}>
                    <div className={cn(styles.item, styles.label)}>高音</div>
                    <div className={cn(styles.item, styles.label)}>中音</div>
                    <div className={cn(styles.item, styles.label)}>低音</div>
                </div>

                <div className={styles.musicalNote}>
                    {
                        musicalNoteList.map((item) => {
                            return (
                                <MusicalNoteItem
                                    {...item}
                                    key={item.type}
                                    curKeyboard={keyboard}
                                    className={styles.item}
                                />
                            );
                        })
                    }
                </div>
            </div>
        </div>
    );
}

export default YuanShenPinao;
